package thred;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class demo3 {
    // 访问计数器 volatile关键字保证了该变量可见性
    volatile static int count = 0;

    /**
     * 该方法添加上了synchronized关键字，虽然达到了我们预期的效果 ，但是耗时太长了
     * 出现耗时长的原因在于在方法中我们TimeUnit.MILLISECONDS.sleep(5);使得程序
     * 需要睡眠5ms，而加上了关键字之后变为单线程环境，每个线程都需要睡眠5ms
     *
     * 解决方式：
     *
     * @throws InterruptedException
     */
    // 模拟用户的请求方法
    public static void request() throws InterruptedException {
        // 延迟5ms，模拟用户访问网站
        TimeUnit.MILLISECONDS.sleep(5);
        // 模拟后台访问计数
        int expriedCount;
        while (!compareAndSwap(expriedCount=getCount(),expriedCount+1)) {}

    }

    public static boolean compareAndSwap(int expriedCount,int newCount) {
        if (getCount() == expriedCount) {
            count = newCount;
            return true;
        }
        return false;
    }

    public static int getCount() {return count;}

    public static void main(String[] args) throws InterruptedException {
        // 记录开始时间
        long startTime = System.currentTimeMillis();

        // 定义线程数
        int threadSize = 100;

        // 定义门闩 jdk封装的计数器 底层通过AQS实现的线程安全
        CountDownLatch countDownLatch = new CountDownLatch(threadSize);

        // 开启100个线程
        for (int i = 0; i < threadSize; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    // 模拟用户的10次访问
                    for (int j = 0; j < 10; j++) {
                        try {
                            request();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } finally {
                            // 计数-1
                            countDownLatch.countDown();
                        }
                    }
                }
            });
            // 启动线程
            thread.start();
        }

        // 保证当100个线程执行完毕时，执行后面的代码
        countDownLatch.await();

        // 记录结束时间
        long endTime = System.currentTimeMillis();

        // 打印最后一个执行的线程+最终输出的结果数
        System.out.println(Thread.currentThread().getName()+" count: "+count + " totalTime: "+(endTime-startTime));
    }
}
